﻿using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.IO;
using System.Text;
using System.Windows.Forms;

namespace TypedDataSetGen
{
    public partial class FormMain : Form
    {
        private const string ProjectPathFileName = @"\ProjectPath.txt";
        private const string ConnStrPathFileName = @"\ConnStrPathFileName.txt";
        private SqlConnection oCn;

        public FormMain()
        {
            InitializeComponent();
        }

        private void FormMain_Load(object sender, EventArgs e)
        {
            OpenConnectionAndLoadSchemas();
            GetProjectPath();
            GetConnString();
        }

        private void GetProjectPath()
        {
            string fullPath = Application.StartupPath + ProjectPathFileName;
            if (File.Exists(fullPath))
            {
                TextReader tr = new StreamReader(fullPath, Encoding.Default);
                txtPath.Text = tr.ReadLine();
                tr.Close();
            }
        }

        private void GetConnString()
        {
            string fullPath = Application.StartupPath + ConnStrPathFileName;
            if (File.Exists(fullPath))
            {
                TextReader tr = new StreamReader(fullPath, Encoding.Default);
                txtConnStr.Text = tr.ReadLine();
                tr.Close();
            }
        }

        private void btnGetTables_Click(object sender, EventArgs e)
        {
            TableListDoldur();
        }

        private bool OpenConnectionAndLoadSchemas()
        {
            txtSchema.AutoCompleteCustomSource.Clear();
            if (oCn != null) { if (oCn.State == ConnectionState.Open) oCn.Close(); }
            oCn = new SqlConnection(txtConnStr.Text);
            try
            {
                oCn.Open();
                LoadSchemas();
                labelConnectedDb.ForeColor = Color.Blue;
                labelConnectedDb.Text = "Connected To " + oCn.Database;
                return true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                labelConnectedDb.ForeColor = Color.DarkRed;
                labelConnectedDb.Text = "DataBase Connection Failed";
                return false;
            }
        }

        private void LoadSchemas()
        {
            string sql = "SELECT S.name AS SchemaName FROM sys.schemas AS S WHERE S.name LIKE '%' ORDER BY S.name";
            SqlCommand cmd = new SqlCommand(sql, oCn);
            SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.Default);
            DataTable oDt = new DataTable();
            oDt.Load(dr);

            foreach (DataRow rw in oDt.Rows)
            {
                txtSchema.AutoCompleteCustomSource.Add(rw["SchemaName"].ToString());
            }
        }

        private void TableListDoldur()
        {
            if (oCn != null)
            {
                lstTables.Items.Clear();
                string sSql = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES";
                if (txtSchema.Text != "(ALL)" && txtSchema.Text.Trim() != "")
                    sSql += " WHERE TABLE_SCHEMA = '" + txtSchema.Text.Replace("'", "") + "'";
                sSql += " ORDER BY TABLE_NAME";
                SqlCommand cmd = new SqlCommand(sSql, oCn);
                SqlDataReader da = cmd.ExecuteReader(CommandBehavior.Default);
                while (da.Read())
                {
                    lstTables.Items.Add(da.GetString(0));
                }
                da.Close();
            }
        }

        private void btnRun_Click(object sender, EventArgs e)
        {
            if (!CheckFields()) return;

            string dsName;
            string tableName;
            string schemaName = txtSchema.Text;
            string dbName = oCn.Database;

            if (!Directory.Exists(txtPath.Text)) Directory.CreateDirectory(txtPath.Text);

            foreach (object lstItem in lstTables.SelectedItems)
            {

                tableName = lstItem.ToString();
                dsName = tableName + txtSuffix.Text;

                StreamWriter txt;

                try
                {
                    txt = new StreamWriter(txtPath.Text + "\\" + dsName + ".cs");
                }
                catch (UnauthorizedAccessException authEx)
                {
                    MessageBox.Show(authEx.Message);
                    return;
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.ToString());
                    return;
                }

                DataSet ds = new DataSet();
                try
                {
                    string ssql = "Select * From " + dbName + "." + schemaName + "." + tableName;
                    SqlDataAdapter da = new SqlDataAdapter(ssql, oCn);
                    da.FillSchema(ds, SchemaType.Mapped, tableName);
                    txt.Write(GenerateDataSet(ds, txtNamespace.Text, tableName, schemaName, dbName));
                    MessageBox.Show(tableName + " Tablosuna ait Class başarıyla oluşturuldu.", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
                txt.Close();
            }
        }

        private bool CheckFields()
        {
            string errMsg = string.Empty;

            if (lstTables.SelectedItem == null) errMsg = "Önce bir tablo seçin !";
            if (lstTables.Items.Count == 0) errMsg = "Önce bir Schema adı yazın ve 'Get Table Information' butonuna tıklayarak schema altındaki tabloları listeleyin ve bu tablolardan birisini seçin !";
            if (txtAttributesNameSpace.Text == string.Empty) errMsg = "Attributes Namespace alanı boş bırakılamaz !";
            if (txtSuffix.Text == string.Empty) errMsg = "Suffix alanı boş bırakılamaz !";
            if (txtPath.Text == string.Empty) errMsg = "Target Path alanı boş bırakılamaz !";
            if (txtNamespace.Text == string.Empty) errMsg = "Namespace alanı boş bırakılamaz !";

            if (errMsg != string.Empty)
            {
                MessageBox.Show(errMsg, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return false;
            }
            return true;
        }

        private CodeTypeMember FindMember(string sMemberName, IList src)
        {

            foreach (CodeTypeMember cdtyp in src)
            {
                if (cdtyp.Name == sMemberName)
                    return cdtyp;
            }
            return null;
        }

        private string GenerateDataSet(DataSet ds, string sNamespace, string pTableName, string pSchemaName, string pDatabaseName)
        {
            Microsoft.CSharp.CSharpCodeProvider cp = new Microsoft.CSharp.CSharpCodeProvider();
            CodeNamespace cns = new CodeNamespace(sNamespace);

            //get autoincrement column if available
            string sAutoIncColName = this.GetAutoIncrementColumnName(pDatabaseName + "." + pSchemaName + "." + pTableName);

            //set autoincrement seed and step to -1 if checked by user
            if (!String.IsNullOrEmpty(sAutoIncColName) && cbSetAutoIncrementSeedAndStepToMinusOne.Checked)
            {
                //2+3 = 5 but 3+2 != 5 ???
                ds.Tables[0].Columns[sAutoIncColName].AutoIncrementStep = -1;
                ds.Tables[0].Columns[sAutoIncColName].AutoIncrementSeed = -1;
            }

            using (StringWriter schemaWr = new StringWriter())
            {
                ds.WriteXmlSchema(schemaWr);
                System.Data.Design.TypedDataSetGenerator.Generate(schemaWr.ToString(), new CodeCompileUnit(), cns, cp, System.Data.Design.TypedDataSetGenerator.GenerateOption.LinqOverTypedDatasets);
                //System.Data.Design.TypedDataSetGenerator.Generate(ds, cns, cp);
            }


            string name = cns.Types[0].Name;

            //Dataset'in tip ismini degistir
            name = ds.Tables[0].TableName + txtSuffix.Text;
            cns.Types[0].Name = name;


            //AddTableSchemaAttribute(cns.Types[0], pSchemaName);
            //AddDatabaseAttribute(cns.Types[0], pDatabaseName);


            CodeMemberMethod oClone = (CodeMemberMethod)FindMember("Clone", cns.Types[0].Members);
            CodeCastExpression exp =
              new CodeCastExpression(name,
               new CodeMethodInvokeExpression(
                new CodeMethodReferenceExpression(
                 new CodeBaseReferenceExpression(), "Clone"),
                  new CodePrimitiveExpression[] { }));

            CodeVariableDeclarationStatement cvds = new CodeVariableDeclarationStatement(name, "cln", exp);
            oClone.Statements[0] = cvds;


            //System.ComponentModel.ToolboxItem attribute  u her zaman 3. indexde bulunmayaiblir, bu nedenle hata vardı. aşağıdaki şekilde değiştirildi
            foreach (CodeAttributeDeclaration cad in cns.Types[0].CustomAttributes)
                if (cad.Name == "System.ComponentModel.ToolboxItem")
                {
                    ((CodePrimitiveExpression)cad.Arguments[0].Value).Value  = false;
                    break;
                }
            

            // DataTable'ı bul. 
            // Attribute'ları ekle. 

            CodeTypeDeclaration oDataTable = (CodeTypeDeclaration)FindMember(pTableName + "DataTable", cns.Types[0].Members);

            AddTableSchemaMember(oDataTable, pSchemaName);
            AddDatabaseMember(oDataTable, pDatabaseName);
            AddFullTableNameMember(oDataTable, pDatabaseName + "." + pSchemaName + "." + pTableName);
            AddFullTableNameAttribute(oDataTable, pDatabaseName + "." + pSchemaName + "." + pTableName);

            // Add haSAutoIcrementColumn and AutoIncrementColumnName properties...


            AddAutoIncrementColNameMember(oDataTable, sAutoIncColName);
            if (sAutoIncColName == string.Empty)
                AddHasAutoIncrementMember(oDataTable, false);
            else
                AddHasAutoIncrementMember(oDataTable, true);

            AddAutoIncrementColumnNameAttribute(oDataTable, sAutoIncColName);



            //Initvars methodundaki dataset tip ismini degistir.
            CodeMemberMethod oInitClass = (CodeMemberMethod)FindMember("InitClass", cns.Types[0].Members);

            ((CodeAssignStatement)oInitClass.Statements[0]).Right = new CodePrimitiveExpression(name);

            //GetTypedDataSetSchema methodundaki dataset tip ismini degistir.
            CodeMemberMethod oGetTypedDataSetSchema = (CodeMemberMethod)FindMember("GetTypedDataSetSchema", cns.Types[0].Members);
            CodeObjectCreateExpression exp2 = new CodeObjectCreateExpression(name, new CodePrimitiveExpression[] { });
            CodeVariableDeclarationStatement cvds2 = new CodeVariableDeclarationStatement(name, "ds", exp2);
            oGetTypedDataSetSchema.Statements[0] = cvds2;

            //Tablonun icindeki GetTypedTableSchema methodundaki tipi degistirir.
            oGetTypedDataSetSchema = (CodeMemberMethod)FindMember("GetTypedTableSchema", ((CodeTypeDeclaration)FindMember(ds.Tables[0].TableName + "DataTable", cns.Types[0].Members)).Members);
            exp2 = new CodeObjectCreateExpression(name, new CodePrimitiveExpression[] { });
            cvds2 = new CodeVariableDeclarationStatement(name, "ds", exp2);
            oGetTypedDataSetSchema.Statements[2] = cvds2;


            StringBuilder sb = new StringBuilder();
            TextWriter tw = new StringWriter(sb);
            CodeGeneratorOptions cgo = new CodeGeneratorOptions();
            cp.GenerateCodeFromNamespace(cns, tw, cgo);
            return sb.ToString();
        }

        private void btnFile_Click(object sender, EventArgs e)
        {
            folderBrowserDialog1.ShowDialog();
            txtPath.Text = folderBrowserDialog1.SelectedPath;
        }

        private void buttonCreateCacheItemStrings_Click(object sender, EventArgs e)
        {
            textBoxCacheItemServer.Text = "";
            textBoxCacheItemWeb.Text = "";

            if (lstTables.SelectedItem == null) return;
            string nameSpace = txtNamespace.Text;
            string dsName = lstTables.SelectedItem.ToString() + txtSuffix.Text;

            string server = string.Format(@"<CacheItem CacheItemName='{0}' TypedDatasetName='{1}.{2}, IB.EBakanlik.Types' MinutesToExpire='0' />", dsName, nameSpace, dsName);
            string web = string.Format(@"<CacheItem CacheItemName='{0}' MinutesToExpire='0' />", dsName);

            textBoxCacheItemServer.Text = server.Replace("'", ((char)34).ToString());
            textBoxCacheItemWeb.Text = web.Replace("'", ((char)34).ToString());
        }

        private void buttonConnectToDb_Click(object sender, EventArgs e)
        {
            if (OpenConnectionAndLoadSchemas())
                MessageBox.Show("Veritabanı bağlantısı yapıldı.", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void buttonSaveProjectPath_Click(object sender, EventArgs e)
        {
            string fullPath = Application.StartupPath + ProjectPathFileName;
            TextWriter tw = new StreamWriter(fullPath, false, Encoding.Default);
            tw.WriteLine(txtPath.Text);
            tw.Close();
            MessageBox.Show("DataSet kayıt yeri kaydedildi.", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void lstTables_SelectedIndexChanged(object sender, EventArgs e)
        {

        }

        private void buttonSaveConnStr_Click(object sender, EventArgs e)
        {
            if (txtConnStr.Text == string.Empty)
            {
                MessageBox.Show("Write ConnectionString!", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            string fullPath = string.Join("", Application.StartupPath, ConnStrPathFileName);
            TextWriter tw = new StreamWriter(fullPath, false, Encoding.Default);
            tw.WriteLine(txtConnStr.Text);
            tw.Close();
            MessageBox.Show("ConnectionString was saved.", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        
        private void AddFullTableNameAttribute(CodeTypeDeclaration cd, string pFullTableName)
        {

            CodeAttributeArgument[] arguments = new CodeAttributeArgument[] { new CodeAttributeArgument(new CodePrimitiveExpression(pFullTableName)) };
            CodeAttributeDeclaration codeAttrDecl = new CodeAttributeDeclaration(txtAttributesNameSpace.Text + ".FullTableName", arguments);
            cd.CustomAttributes.Add(codeAttrDecl);
        }

        private void AddAutoIncrementColumnNameAttribute(CodeTypeDeclaration cd, string pColumnName)
        {

            CodeAttributeArgument[] arguments = new CodeAttributeArgument[] { new CodeAttributeArgument(new CodePrimitiveExpression(pColumnName)) };
            CodeAttributeDeclaration codeAttrDecl = new CodeAttributeDeclaration(txtAttributesNameSpace.Text + ".AutoIncColName", arguments);
            cd.CustomAttributes.Add(codeAttrDecl);
        }

        private void AddTableSchemaMember(CodeTypeDeclaration cd, string pSchemaName)
        {

            CodeMemberField tableSchemaName = new CodeMemberField("System.String", "TableSchemaName");
            tableSchemaName.InitExpression = new CodePrimitiveExpression(pSchemaName);
            tableSchemaName.Attributes = MemberAttributes.Const | MemberAttributes.Public;
            tableSchemaName.Comments.Add(AddComment());
            cd.Members.Add(tableSchemaName);
        }

        private void AddDatabaseMember(CodeTypeDeclaration cd, string pDatabaseName)
        {

            CodeMemberField databaseName = new CodeMemberField("System.String", "DbName");
            databaseName.InitExpression = new CodePrimitiveExpression(pDatabaseName);
            databaseName.Attributes = MemberAttributes.Const | MemberAttributes.Public;
            databaseName.Comments.Add(AddComment());
            cd.Members.Add(databaseName);
        }

        private void AddFullTableNameMember(CodeTypeDeclaration cd, string pFullTableName)
        {

            CodeMemberField tableFullName = new CodeMemberField("System.String", "TableFullName");
            tableFullName.InitExpression = new CodePrimitiveExpression(pFullTableName);
            tableFullName.Attributes = MemberAttributes.Const | MemberAttributes.Public;
            tableFullName.Comments.Add(AddComment());
            cd.Members.Add(tableFullName);
        }

        private void AddHasAutoIncrementMember(CodeTypeDeclaration cd, bool pHasCol)
        {

            CodeMemberField hasAutoIncCol = new CodeMemberField("System.Boolean", "HasAutoIncrementColumn");
            hasAutoIncCol.InitExpression = new CodePrimitiveExpression(pHasCol);
            hasAutoIncCol.Attributes = MemberAttributes.Const | MemberAttributes.Public;
            hasAutoIncCol.Comments.Add(AddComment());
            cd.Members.Add(hasAutoIncCol);
        }

        private void AddAutoIncrementColNameMember(CodeTypeDeclaration cd, string pColName)
        {
            CodeMemberField AutoIncColName = new CodeMemberField("System.String", "AutoIncrementColumnName");
            AutoIncColName.InitExpression = new CodePrimitiveExpression(pColName);
            AutoIncColName.Attributes = MemberAttributes.Const | MemberAttributes.Public;
            AutoIncColName.Comments.Add(AddComment());
            cd.Members.Add(AutoIncColName);
        }

        private CodeCommentStatement AddComment()
        {
            CodeComment comm = new CodeComment("This custom member was added by TypedDataSetGen tool.", false);
            CodeCommentStatement cst = new CodeCommentStatement(comm);
            return cst;

        }

        private string GetAutoIncrementColumnName(string pFullTableName)
        {
            SqlConnection cnn = new SqlConnection(txtConnStr.Text);
            cnn.Open();
            SqlCommand cmdd = new SqlCommand("select * from sys.columns " +
                " where object_id = OBJECT_ID ('" + pFullTableName + "') and " +
                " is_identity = 1 ");

            cmdd.Connection = cnn;
            SqlDataAdapter dap = new SqlDataAdapter(cmdd);
            DataSet dss = new DataSet();
            dap.Fill(dss);

            if (dss.Tables[0].Rows.Count <= 0)
                return string.Empty;

            return dss.Tables[0].Rows[0]["name"].ToString();

        }

        //private void AddTableSchemaAttribute(CodeTypeDeclaration cd, string pSchemaName)
        //{

        //    CodeAttributeArgument[] arguments = new CodeAttributeArgument[] { new CodeAttributeArgument(new CodePrimitiveExpression(pSchemaName)) };
        //    CodeAttributeDeclaration codeAttrDecl = new CodeAttributeDeclaration(txtSchemaNameAttributeNS.Text + ".SchemaName", arguments);
        //    cd.CustomAttributes.Add(codeAttrDecl);
        //}

        //private void AddDatabaseAttribute(CodeTypeDeclaration cd, string pDatabaseName)
        //{

        //    CodeAttributeArgument[] arguments = new CodeAttributeArgument[] { new CodeAttributeArgument(new CodePrimitiveExpression(pDatabaseName)) };
        //    CodeAttributeDeclaration codeAttrDecl = new CodeAttributeDeclaration(txtDatabaseNameAttributeNS.Text + ".DatabaseName", arguments);
        //    cd.CustomAttributes.Add(codeAttrDecl);
        //}
    }
}
